#include "RTSPStreamClient.h"

unsigned int totNumPacketReceived = ~0;

RTSPStreamClient::RTSPStreamClient(string pgName, string url, string confFile)
{
	confFileName = confFile;

	ccfa.audioOnly = false;
	ccfa.videoOnly = false;
	ccfa.singleMedium = "";
	ccfa.verbosityLevel = 1;
	ccfa.duration = 0;
	ccfa.durationSlop = -1.0;
	ccfa.initialSeekTime = 0.0;
	ccfa.scale = 1.0;
	ccfa.interPacketGapMaxTime = 0;
	ccfa.playContinuously = false;
	ccfa.notifyOnPacketArrival = false;
	ccfa.username = "";
	ccfa.password = "";
	ccfa.proxyServerName = "";
	ccfa.proxyServerPortNum = 0;
	ccfa.fileSinkBufferSize = 20000;
	ccfa.socketInputBufferSize = 0;
	ccfa.qosMeasurementIntervalMS =0;
	ccfa.displayQOS = false;
	ccfa.desiredPortNum = 0;

	env = NULL;
	scheduler = NULL;
	progName = pgName;
	urlToPlay = url;
	okForDescribe = false;
	madeProgress = false;
	firstTime = true;
	gettimeofday(&startTime, NULL);
	nextQOSMeasurementUSecs = 1;
	qosRecordHead = NULL;


	sessionTimerTask = NULL;
	arrivalCheckTimerTask = NULL;
	interPacketGapCheckTimerTask = NULL;
}

RTSPStreamClient::~RTSPStreamClient()
{
	//Nothing to Destroy
}


void RTSPStreamClient::readConfFile()
{
	string extension;

	extension = confFileName.substr((confFileName.size()-3), 3);
	if(extension.compare("mrc")!=0)
	{
		cerr << "ERROR : The file name you have provided is not a correct MediaRiver Configuration file " << endl << "Please provide a correct .mrc file !" << endl;
		pthread_exit(0);
	}
	else
	{
		confFile.open(confFileName.c_str(), ios::in);
		if(!confFile.fail())
		{
			string buffer1;
			getline(confFile, buffer1);
			if((buffer1.compare("#MediaRiverClient-ConfFile"))==0)
			{
				getline(confFile, buffer1);
				if((buffer1.compare("#V1"))==0)
				{
					while(!confFile.eof())
					{
						getline(confFile, buffer1);
						if(buffer1.compare("")!=0)
						{
							if(buffer1.at(0)!='#')
							{
								string buffer2;
								istringstream iss(buffer1);
								getline(iss, buffer2, '=');

								if(buffer2.compare("audioOnly")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									if(buffer3.compare("0")==0)
									{
										ccfa.audioOnly = false;
										ccfa.singleMedium = "";
									}
									else if(buffer3.compare("1")==1)
									{
										ccfa.audioOnly = true;
										ccfa.singleMedium = "audio";
									}
									else
									{
										cerr << "WARNING : The audioOnly parameters has been set with an incorrect value in the configuration file" << endl;
										ccfa.audioOnly = false;
									}
								}
								else if(buffer2.compare("videoOnly")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									if(buffer3.compare("0")==0)
									{
										ccfa.videoOnly = false;
										ccfa.singleMedium = "";
									}
									else if(buffer3.compare("1")==1)
									{
										ccfa.videoOnly = true;
										ccfa.singleMedium = "video";
									}
									else
									{
										cerr << "WARNING : The videoOnly parameters has been set with an incorrect value in the configuration file" << endl;
										ccfa.videoOnly = false;
									}
								}
								else if(buffer2.compare("playContinuously")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									if(buffer3.compare("0")==0)
									{
										ccfa.playContinuously = false;
									}
									else if(buffer3.compare("1")==1)
									{
										ccfa.playContinuously = true;
									}
									else
									{
										cerr << "WARNING : The playContinuously parameters has been set with an incorrect value in the configuration file" << endl;
										ccfa.playContinuously = false;
									}
								}
								else if(buffer2.compare("interPacketGapMaxTime")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									unsigned int nb;
									istringstream iss2(buffer3);
									iss2 >> nb;
									ccfa.interPacketGapMaxTime = nb;
								}
								else if(buffer2.compare("outputBufferSize")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									unsigned int nb;
									istringstream iss2(buffer3);
									iss2 >> nb;
									ccfa.fileSinkBufferSize = nb;
								}
								else if(buffer2.compare("inputBufferSize")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									unsigned int nb;
									istringstream iss2(buffer3);
									iss2 >> nb;
									ccfa.socketInputBufferSize = nb;
								}
								else if(buffer2.compare("qosMeasurementIntervalMS")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									unsigned int nb;
									istringstream iss2(buffer3);
									iss2 >> nb;
									ccfa.qosMeasurementIntervalMS = nb;
								}
								else if(buffer2.compare("verbosityLevel")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									int nb;
									istringstream iss2(buffer3);
									iss2 >> nb;
									ccfa.verbosityLevel = nb;
								}
								else if(buffer2.compare("notifyOnPacketArrival")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									if(buffer3.compare("0")==0)
									{
										ccfa.notifyOnPacketArrival = false;
									}
									else if(buffer3.compare("1")==1)
									{
										ccfa.notifyOnPacketArrival = true;
									}
									else
									{
										cerr << "WARNING : The notifyOnPacketArrival parameters has been set with an incorrect value in the configuration file" << endl;
										ccfa.notifyOnPacketArrival = false;
									}
								}
								else if(buffer2.compare("displayQOS")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									if(buffer3.compare("0")==0)
									{
										ccfa.displayQOS = false;
									}
									else if(buffer3.compare("1")==1)
									{
										ccfa.displayQOS = true;
									}
									else
									{
										cerr << "WARNING : The displayQOS parameters has been set with an incorrect value in the configuration file" << endl;
										ccfa.displayQOS = false;
									}
								}
								else if(buffer2.compare("duration")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									double nb;
									istringstream iss2(buffer3);
									iss2 >> nb;
									if(nb < 0)
									{
										ccfa.duration = 0;
										ccfa.durationSlop = (-nb);
									}
									else if (nb >= 0)
									{
										ccfa.duration = nb;
										ccfa.durationSlop = 0;
									}

								}
								else if(buffer2.compare("initialSeekTime")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									double nb;
									istringstream iss2(buffer3);
									iss2 >> nb;
									ccfa.initialSeekTime = nb;
								}
								else if(buffer2.compare("scale")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									double nb;
									istringstream iss2(buffer3);
									iss2 >> nb;
									ccfa.scale = nb;
								}
								else if(buffer2.compare("username")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									ccfa.username = buffer3;
								}
								else if(buffer2.compare("password")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									ccfa.password = buffer3;
								}
								else if(buffer2.compare("proxyServerName")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									ccfa.proxyServerName = buffer3;
								}
								else if(buffer2.compare("proxyServerPortNum")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									unsigned short nb;
									istringstream iss2(buffer3);
									iss2 >> nb;

									if(nb < 0 || nb >= 65536 || nb&1)
									{
										cerr << "ERROR : The specified port number \" " << nb << " \" is not correct ! " << endl << "Port Number must be in the range (0, 65536) and even" << endl;
										pthread_exit(0);
									}

									ccfa.proxyServerPortNum = nb;
								}
								else if(buffer2.compare("RTPPortNum")==0)
								{
									string buffer3;
									getline(iss, buffer3, '=');
									unsigned short nb;
									istringstream iss2(buffer3);
									iss2 >> nb;

									if(nb < 0 || nb >= 65536 || nb&1)
									{
										cerr << "ERROR : The specified port number \" " << nb << " \" is not correct ! " << endl << "Port Number must be in the range (0, 65536) and even" << endl;
										pthread_exit(0);
									}

									ccfa.desiredPortNum = nb;
								}
							}
						}
					}
				}
			}
			else
			{
				cerr << "ERROR : Cannot read the configuration file \"" << confFileName << "\"" << endl << "The file is not a correct configuration file" << endl;
				pthread_exit(0);
			}
		}
		else
		{
			cerr << "ERROR : Cannot read the configuration file \"" << confFileName << "\"" << endl << "The file is not a correct configuration file" << endl;
			pthread_exit(0);
		}
	}
}



void RTSPStreamClient::configureClient()
{
	scheduler = BasicTaskScheduler::createNew();
	env = BasicUsageEnvironment::createNew(*scheduler);

	if(ccfa.durationSlop < 0)
	{
		ccfa.durationSlop = ccfa.qosMeasurementIntervalMS > 0 ? 0.0 : 5.0;
	}

	rtspClient = RTSPClient::createNew(*env, ccfa.verbosityLevel, progName.c_str(), 0);
	if(rtspClient == NULL)
	{
		cerr << "ERROR : Cannot create a RTSP client ! " << endl << "Message : \"" << env->getResultMsg() << "\"" << endl;
		pthread_exit(0);
	}
}



void RTSPStreamClient::sendOptionRequest()
{
	char* optionResponse;
	char* userNameNotConst = (char*) ccfa.username.c_str();
	char* passwordNotConst = (char*)ccfa.password.c_str();
	optionResponse = rtspClient->sendOptionsCmd(urlToPlay.c_str(), userNameNotConst, passwordNotConst);

	if(optionResponse == NULL)
	{
		cerr << "The OPTIONS request has failed !" << endl << "Message : \"" << env->getResultMsg() << "\"" << endl;
		pthread_exit(0);
	}
	else
	{
		string optionResponse2;
		optionResponse2 = optionResponse;
		delete[] optionResponse;

		if(ccfa.verbosityLevel >= 1)
		{
			cout << "The OPTIONS Request has returned : " << endl << "\"" << optionResponse2 << "\"" << endl;
		}

		size_t requestResult;
		requestResult = optionResponse2.find("DESCRIBE");
		if(requestResult != string::npos)
		{
			okForDescribe = true;
		}
		else
		{
			okForDescribe = false;
		}
	}
}


void RTSPStreamClient::sendDescribeRequest()
{
	if(okForDescribe == true)
	{
		char* sdpDescription2;

		if(ccfa.username.compare("") && ccfa.password.compare(""))
		{
			sdpDescription2 = rtspClient->describeURL(urlToPlay.c_str());
		}
		else
		{
			sdpDescription2 = rtspClient->describeWithPassword(urlToPlay.c_str(), ccfa.username.c_str(), ccfa.password.c_str());
		}

		statusCode = rtspClient->describeStatus();

		if(sdpDescription2 == NULL)
		{
			cerr << "The DESCRIBE request has failed !" << endl << "Message : \"" << env->getResultMsg() << "\"" << endl;
			pthread_exit(0);
		}
		else
		{
			if(ccfa.verbosityLevel >= 1)
			{
				cout << "The DESCRIBE request for the url \" " << urlToPlay << " \" has returned : " << endl << sdpDescription2 << endl;
			}
			sdpDescription = sdpDescription2;
		}
	}
	else
	{
		cerr << "The DESCRIBE cannot be sent" << endl << "The server doesn't accept this request" << endl;
		pthread_exit(0);
	}
}



void RTSPStreamClient::createNewInputReceiver()
{
	session = MediaSession::createNew(*env, sdpDescription.c_str());
	if(session == NULL)
	{
		cerr << "ERROR : The MediaSession for the url \" " << urlToPlay << " \" cannot be created !" << endl << "Message : \"" << env->getResultMsg() << "\"" << endl;
		pthread_exit(0);
	}
	else if(session->hasSubsessions()==false)
	{
		cerr << "ERROR : The RTSP session for the url \" " << urlToPlay << " \" has no media subsession !" << endl << "Cannot start any streaming !" << endl;
		pthread_exit(0);
	}

	MediaSubsessionIterator iter(*session);
	MediaSubsession* subsession;
	madeProgress = false;
	string singleMediumToTest = ccfa.singleMedium;

	while((subsession = iter.next()) != NULL)
	{
		if(singleMediumToTest.compare("")!=0)
		{
			if(singleMediumToTest.compare(subsession->mediumName())!=0)
			{
				if(ccfa.verbosityLevel>=1)
				{
					cout << "This is a single " << ccfa.singleMedium << "session only !" << "Therefore, the session " << subsession->mediumName() << " - " << subsession->codecName() << " will be ignored" << endl;
				}
				continue;
			}
			else
			{
				singleMediumToTest = "xxxxx";
			}
		}

		if(ccfa.desiredPortNum != 0)
		{
			subsession->setClientPortNum(ccfa.desiredPortNum);
			ccfa.desiredPortNum += 2;
		}

		if(!(subsession->initiate(-1)))
		{
			cerr << "ERROR : Cannot create RTP receiver for \" " << subsession->mediumName() << " - " << subsession->codecName() << " \"" << endl << "Message : \" " << env->getResultMsg() << " \"" << endl;
		}
		else
		{
			if(ccfa.verbosityLevel >= 1)
			{
				cout << "RTP receiver created for \" " << subsession->mediumName() << " - " << subsession->codecName() << " \" " << endl << "Port : " << subsession->clientPortNum() << " - " << subsession->clientPortNum()+1 << endl;
			}
			madeProgress = true;

			if(subsession->rtpSource() != NULL)
			{
				const unsigned int tresh = 1000000;
				subsession->rtpSource()->setPacketReorderingThresholdTime(tresh);

				if(ccfa.socketInputBufferSize > 0)
				{
					int socketNum;
					unsigned int curBufferSize;
					unsigned int newBufferSize;

					socketNum = subsession->rtpSource()->RTPgs()->socketNum();
					curBufferSize = getReceiveBufferSize(*env, socketNum);
					newBufferSize = setReceiveBufferTo(*env, socketNum, ccfa.socketInputBufferSize);

					if(ccfa.verbosityLevel >= 1)
					{
						cout << "The input receiver socket size has been changed for \" " << subsession->mediumName() << " - " << subsession->codecName() << " \" " << endl << "Socket from " << curBufferSize << " to " << newBufferSize << " bytes" << endl;
					}
				}
			}
		}
	}

	if(madeProgress == false)
	{
		pthread_exit(0);
	}
}



void RTSPStreamClient::configureInputReceiver()
{
	MediaSubsessionIterator iter(*session);
	MediaSubsession* subsession;

	while( (subsession = iter.next()) !=NULL )
	{
		if(subsession->clientPortNum() == 0)
		{
			continue;
		}

		bool result;
		result = rtspClient->setupMediaSubsession(*subsession, false, false);

		if(result == false)
		{
			cerr << "ERROR : Cannot configure \" " << subsession->mediumName() << " - " << subsession->codecName() << " \" " << endl << "Message : \" " << env->getResultMsg() << " \"" << endl;
			pthread_exit(0);
		}
		else
		{
			if(ccfa.verbosityLevel >= 1)
			{
				cout << "The subsession : \" " << subsession->mediumName() << " - " << subsession->codecName() << " \" " << endl << "has been configured sucessfully !" << "Port : " << subsession->clientPortNum() << " - " << subsession->clientPortNum()+1 << endl;
			}
		}
	}
}



void RTSPStreamClient::createNewOutputSink()
{
	MediaSubsessionIterator iter(*session);
	MediaSubsession* subsession;
	iter.reset();

	while( (subsession = iter.next()) != NULL )
	{
		string mediumName;
		string codecName;
		//MRSink *outputSink; //HACK

		mediumName = subsession->mediumName();
		codecName = subsession->codecName();

		if(subsession->readSource() == NULL)
		{
			continue;
		}

		if((mediumName.compare("video")==0) && (codecName.compare("H264")==0))
		{
			//outputSink = H264VideoFileSink::createNew(*env,"stdout",ccfa.fileSinkBufferSize,false);
			outputSink = MR264Sink::createNew(*env,ccfa.fileSinkBufferSize);
		}
		else
		{
			//outputSink = FileSink::createNew(*env,"stdout",ccfa.fileSinkBufferSize,false);
			outputSink = MRSink::createNew(*env,ccfa.fileSinkBufferSize);
		}

		subsession->sink = outputSink;

		if(subsession->sink == NULL)
		{
			cerr << "ERROR : Cannot create an output sink for \" " << subsession->mediumName() << " - " << subsession->codecName() << " \" " << endl << "Message : \" " << env->getResultMsg() << " \"" << endl;
			pthread_exit(0);
		}
		else
		{
			if(ccfa.verbosityLevel >= 1)
			{
				cout << "Outputting data from \" " << subsession->mediumName() << " - " << subsession->codecName() << " \" to stdout" << endl;
			}
		}
	}
}

void RTSPStreamClient::startPlaying()
{
	////////////////////////////////////////
	MediaSubsessionIterator iter(*session);
	MediaSubsession* subsession;
	iter.reset();

	if(firstTime == true)
	{
		while( (subsession = iter.next()) != NULL )
		{
			afterPlayingArgs* apa = new afterPlayingArgs;
			apa->client = this;
			apa->subsession = subsession;

			subsession->sink->startPlaying( *(subsession->readSource()), subsessionAfterPlaying, apa);
			if(subsession->rtcpInstance() != NULL)
			{
				subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, apa);
			}
		}

		firstTime = false;
	}

	//////////////////////////////////////

	if(ccfa.duration == 0)
	{
		if(ccfa.scale > 0)
		{
			ccfa.duration = session->playEndTime() - ccfa.initialSeekTime;
		}
		else if(ccfa.scale < 0)
		{
			ccfa.duration = ccfa.initialSeekTime;
		}
	}
	if(ccfa.duration < 0)
	{
		ccfa.duration = 0.0;
	}

	//////////////////////////////////////

	double endTime = ccfa.initialSeekTime;
	if(ccfa.scale > 0)
	{
		if(ccfa.duration <= 0)
		{
			endTime = 1.0;
		}
		else
		{
			endTime = ccfa.initialSeekTime + ccfa.duration;
		}
	}
	else
	{
		endTime = ccfa.initialSeekTime - ccfa.duration;
		if(endTime < 0)
		{
			endTime = 0.0;
		}
	}

	if( (rtspClient->playMediaSession(*session, ccfa.initialSeekTime, endTime, (float)ccfa.scale)) == false )
	{
		cerr << "Cannot create a playing session ! " << endl << "Message : " << env->getResultMsg() << endl;
		pthread_exit(0);
	}
	else
	{
		if(ccfa.verbosityLevel >= 1)
		{
			cout << "Started playing session !" << endl;
		}
	}

	////////////////////////////////////////

	if(ccfa.qosMeasurementIntervalMS > 0)
	{
		beginQOSMeasurment();
	}

	////////////////////////////////////////

	bool timerIsBeingUsed = false;
	double secondsToDelay = ccfa.duration;

	if(ccfa.duration > 0)
	{
		const double maxDelayTime = (double) (((unsigned)0x7FFFFFFF)/1000000.0);
		if(ccfa.duration > maxDelayTime)
		{
			cerr << "WARNING : Specified end streaming time \" " << ccfa.duration << " \" is greater than the maximum delay time : \" " << maxDelayTime << " \"" << endl << "Client will not perform a delayed shutdown ! " << endl;
		}
		else
		{
			timerIsBeingUsed = true;
			double absScale = ccfa.scale > 0 ? ccfa.scale : -(ccfa.scale);
			secondsToDelay = ccfa.duration / absScale + ccfa.durationSlop;

			int uSecsToDelay = (int) (secondsToDelay * 1000000.0);
			sessionTimerTask = env->taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*)sessionTimerHandler, (void*) this);
		}
	}

	//////////////////////////////////////////

	if(ccfa.verbosityLevel >= 1)
	{
		cout << "Now receiving streamed data !" << endl;
	}


	checkForPacketArrivalInClass();
	checkInterPacketGapsInClass();

	///////////////////////////////////////////

#ifdef USE_DECODERS

	iter.reset();
	while( (subsession = iter.next()) != NULL )
	{
		DecoderClient* decode = new DecoderClient(subsession->sink);
		decode->configureThread();
		decode->launchThread();
		listOfDecoder.push_back(decode);
		delete decode;
	}
#endif

	///////////////////////////////////////////

	env->taskScheduler().doEventLoop();
}


void checkForPacketArrival(void* clientData)
{
	afterPlayingArgs* apa = (afterPlayingArgs*) clientData;
	apa->client->checkForPacketArrivalInClass();
}


void RTSPStreamClient::checkForPacketArrivalInClass()
{
	if(ccfa.notifyOnPacketArrival == true)
	{
		unsigned int numSubsessionChecked = 0;
		unsigned int numSubsessionWithReceivedData = 0;
		unsigned int numSubsessionThatHaveBeenSynced = 0;

		MediaSubsessionIterator iter(*session);
		MediaSubsession* subsession;

		while((subsession = iter.next()) != NULL)
		{
			++numSubsessionChecked;
			RTPSource* src = subsession->rtpSource();

			if(src == NULL)
			{
				continue;
			}

			if(src->receptionStatsDB().numActiveSourcesSinceLastReset() > 0)
			{
				++numSubsessionWithReceivedData;
			}
			if(src->hasBeenSynchronizedUsingRTCP())
			{
				++numSubsessionThatHaveBeenSynced;
			}
		}

		unsigned int numSubsessionToCheck = numSubsessionChecked;

		bool notifyUser;
		notifyUser = numSubsessionWithReceivedData >= numSubsessionToCheck && numSubsessionThatHaveBeenSynced == numSubsessionChecked;

		if(notifyUser)
		{
			struct timeval timeNow;
			gettimeofday(&timeNow, NULL);
			cout << "Data packets have been received since " << timeNow.tv_sec << " s " << timeNow.tv_usec << " usec" << endl;
		}
		else
		{
			int uSecToDelay = 100000;
			afterPlayingArgs* apa = new afterPlayingArgs;
			apa->client = this;
			apa->subsession = NULL;
			arrivalCheckTimerTask = env->taskScheduler().scheduleDelayedTask(uSecToDelay, (TaskFunc*)checkForPacketArrival, apa);
		}
	}
}


void checkInterPacketGaps(void* clientData)
{
	afterPlayingArgs* apa = (afterPlayingArgs*) clientData;
	apa->client->checkInterPacketGapsInClass();
}



void RTSPStreamClient::checkInterPacketGapsInClass()
{
	if(ccfa.interPacketGapMaxTime != 0)
	{
		unsigned int newToNumPacketsReceived = 0;
		MediaSubsessionIterator iter(*session);
		MediaSubsession* subsession;

		while((subsession = iter.next()) != NULL)
		{
			RTPSource* src = subsession->rtpSource();
			if(src == NULL)
			{
				continue;
			}

			newToNumPacketsReceived += src->receptionStatsDB().totNumPacketsReceived();
		}

		unsigned int toto = totNumPacketReceived;
		if(newToNumPacketsReceived == toto)
		{
			cerr << "Closing session because no packets are now received ! " << endl;
			interPacketGapCheckTimerTask = NULL;
			sessionAfterPlayingInClass();
		}
		else
		{
			afterPlayingArgs* apa = new afterPlayingArgs;
			apa->client = this;
			apa->subsession = NULL;
			totNumPacketReceived = newToNumPacketsReceived;
			interPacketGapCheckTimerTask = env->taskScheduler().scheduleDelayedTask( ccfa.interPacketGapMaxTime * 1000000, (TaskFunc*)checkInterPacketGaps, apa);
		}
	}
}


void RTSPStreamClient::sessionAfterPlayingInClass()
{
#ifdef USE_DECODERS
	for(list<DecoderClient*>::iterator it = listOfDecoder.begin();  it != listOfDecoder.end();  it++)
	{
		DecoderClient* decoder = *it;
		decoder->joinThread();
	}
#endif


	if(ccfa.playContinuously == false)
	{
		shutdownClient(0);
	}
	else
	{
		startPlaying();
	}
}



void subsessionAfterPlaying(void* clientData)
{
	afterPlayingArgs* apa = (afterPlayingArgs*) clientData;
	apa->client->subsessionAfterPlayingInClass(apa->subsession);
}



void RTSPStreamClient::subsessionAfterPlayingInClass(MediaSubsession* subsession)
{
	Medium::close(subsession->sink);
	subsession->sink = NULL;

	MediaSession& session = subsession->parentSession();
	MediaSubsessionIterator iter(session);

	while((subsession = iter.next()) != NULL)
	{
		if(subsession->sink != NULL)
		{
			return;
		}
	}

	sessionAfterPlayingInClass();
}



void RTSPStreamClient::shutdownClient(int code)
{
	//Pour test uniquement
	cout << "Debut ecriture fichier" << endl;
	outputSink->writePacketsInFile();
	cout << "Fin ecriture fichier" << endl;

	if(env!=NULL)
	{
		env->taskScheduler().unscheduleDelayedTask(sessionTimerTask);
		env->taskScheduler().unscheduleDelayedTask(arrivalCheckTimerTask);
		env->taskScheduler().unscheduleDelayedTask(interPacketGapCheckTimerTask);
		//env->taskScheduler().unscheduleDelayedTask(qosMeasurementTimerTask);
	}

	if(ccfa.qosMeasurementIntervalMS > 0)
	{
		//printqosdata
	}

	/////////////////////////////////////////////////////////////

	if(session == NULL)
	{
		return;
	}

	MediaSubsessionIterator iter(*session);
	MediaSubsession* subsession;
	while( (subsession = iter.next()) != NULL)
	{
		Medium::close(subsession->sink);
		subsession->sink = NULL;
	}

	/////////////////////////////////////////////////////////////

	cerr << "TEARDOWN depuis ShutdownClient !!!!" << endl;

	rtspClient->teardownMediaSession(*session);

	Medium::close(session);
	Medium::close(rtspClient);

	pthread_exit(0);
}



void subsessionByeHandler(void* clientData)
{
	afterPlayingArgs* apa = (afterPlayingArgs*) clientData;
	apa->client->subsessionByeHandlerInClass(apa->subsession);
}



void RTSPStreamClient::subsessionByeHandlerInClass(MediaSubsession* subsession)
{
	struct timeval timeNow;
	gettimeofday(&timeNow, NULL);
	unsigned int secsDiff = timeNow.tv_sec - startTime.tv_sec;

	if(ccfa.verbosityLevel >= 1)
	{
		cout << "Received a BYE command from  \" " << subsession->mediumName() << " - " << subsession->codecName() << " \" subsession after " << secsDiff << " seconds" << endl;
	}

	subsessionAfterPlayingInClass(subsession);
}



void sessionTimerHandler(void* clientData)
{
	RTSPStreamClient* rtspClient = (RTSPStreamClient*) clientData;
	rtspClient->sessionTimerHandlerInClass();
}



void RTSPStreamClient::sessionTimerHandlerInClass()
{
	sessionTimerTask = NULL;
	sessionAfterPlayingInClass();
}



void RTSPStreamClient::prepareClient()
{
	readConfFile();
	configureClient();
}



void RTSPStreamClient::playFromURL()
{
	if(urlToPlay.compare("")!=0)
	{
		sendOptionRequest();
		sendDescribeRequest();

		createNewInputReceiver();
		configureInputReceiver();
		createNewOutputSink();

		startPlaying();
	}
	else
	{
		cerr << "ERROR : No URL provided !" << endl;
		pthread_exit(0);
	}
}


void RTSPStreamClient::beginQOSMeasurment()
{
	struct timeval startTimeForQOS;
	gettimeofday(&startTimeForQOS, NULL);

	nextQOSMeasurementUSecs = startTimeForQOS.tv_sec * 1000000 + startTimeForQOS.tv_usec;

	qosMeasurementRecord* qosRecordTail = NULL;

	MediaSubsessionIterator iter(*session);
	MediaSubsession* subsession;

	while((subsession = iter.next()) != NULL)
	{
		RTPSource* src = subsession->rtpSource();
		if(src == NULL)
		{
			continue;
		}

		qosMeasurementRecord* qosRecord = new qosMeasurementRecord(startTimeForQOS, src);

		if(qosRecordHead == NULL)
		{
			qosRecordHead = qosRecord;
		}
		if(qosRecordTail != NULL)
		{
			qosRecordTail->fNext = qosRecord;
		}
		qosRecordTail = qosRecord;
	}

	scheduleNextQOS();
}



void RTSPStreamClient::scheduleNextQOS()
{
	nextQOSMeasurementUSecs += ccfa.qosMeasurementIntervalMS * 1000;

	struct timeval timeNow;
	gettimeofday(&timeNow, NULL);

	unsigned int timeNowUSecs = timeNow.tv_sec*1000000 + timeNow.tv_usec;
	unsigned int usecsToDelay = nextQOSMeasurementUSecs - timeNowUSecs;

	afterPlayingArgs* apa = new afterPlayingArgs;
	apa->client = this;
	apa->subsession = NULL;

	qosMeasurementTimerTask = env->taskScheduler().scheduleDelayedTask(usecsToDelay, (TaskFunc*)periodicQOSMeasurement, NULL);
}


void periodicQOSMeasurement(void* clientData)
{
	afterPlayingArgs* apa = (afterPlayingArgs*) clientData;
	apa->client->periodicQOSMeasurementInClass();
}


void RTSPStreamClient::periodicQOSMeasurementInClass()
{
	struct timeval timeNow;
	gettimeofday(&timeNow, NULL);

	for(qosMeasurementRecord* qosRecord = qosRecordHead;  qosRecord != NULL;  qosRecord = qosRecord->fNext)
	{
		qosRecord->periodicQOSMeasurement(timeNow);
	}
}


void RTSPStreamClient::printQOS()
{
	if(ccfa.displayQOS == true)
	{
		cout << "Now printing QOS Statistics" << endl;

		qosMeasurementRecord* curQOSRecord = qosRecordHead;

		if(session != NULL)
		{
			MediaSubsessionIterator iter(*session);
			MediaSubsession* subsession;

			while((subsession = iter.next()) != NULL)
			{
				RTPSource* src = subsession->rtpSource();
				if(src == NULL)
				{
					continue;
				}

				cout << "***************************" << endl;
				cout << "Statistics for subsession \" " << subsession->mediumName() << " - " << subsession->codecName() << " \" " << endl << endl;

				unsigned int numPacketReceived = 0;
				unsigned int numPacketExpected = 0;

				if(curQOSRecord != NULL)
				{
					numPacketReceived = curQOSRecord->totNumPacketReceived;
					numPacketExpected = curQOSRecord->totNumPacketExpected;
				}

				cout << "Number of packets received : " << numPacketReceived << endl;
				cout << "Number of packets lost     : " << numPacketExpected - numPacketReceived << endl;

				if(curQOSRecord != NULL)
				{
					unsigned int secsDiff = curQOSRecord->measurementEndTime.tv_sec - curQOSRecord->measurementStartTime.tv_sec;
					int usecsDiff = curQOSRecord->measurementEndTime.tv_usec - curQOSRecord->measurementStartTime.tv_usec;
					double measurementTime = secsDiff + usecsDiff/1000000.0;

					cout << "Elasped Measurement Time   : " << measurementTime << endl;
					cout << "Total kBytes received      : " << curQOSRecord->kBytesTotal << endl;
					cout << "Sampling interval in ms    : " << ccfa.qosMeasurementIntervalMS << endl;

					if(curQOSRecord->kbits_per_second_max == 0)
					{
						cout << "Max kbits per seconds      : " << "unavailable" << endl;
						cout << "Min kbits per seconds      : " << "unavailable" << endl;
						cout << "Average kbits per seconds  : " << "unavailable" << endl;
					}
					else
					{
						cout << "Max kbits per seconds      : " << curQOSRecord->kbits_per_second_max << endl;
						cout << "Min kbits per seconds      : " << curQOSRecord->kbits_per_second_min << endl;
						cout << "Average kbits per seconds  : " << (measurementTime == 0.0 ? 0.0 : 8*curQOSRecord->kBytesTotal/measurementTime) << endl;
					}

					cout << "Min packet loss percentage : " << 100*curQOSRecord->packet_loss_fraction_min << endl;

					double packetLossFraction = numPacketExpected == 0 ? 1.0 : 1.0 - numPacketReceived/(double)numPacketExpected;

					if(packetLossFraction < 0.0)
					{
						packetLossFraction = 0.0;
					}

					cout << "Max packet loss percentage : " << (packetLossFraction == 1.0 ? 100.0 : 100*curQOSRecord->packet_loss_fraction_max) << endl;
					cout << "Ave packet loss percentage : " << 100*packetLossFraction << endl;

					RTPReceptionStatsDB::Iterator statsIter(src->receptionStatsDB());
					RTPReceptionStats* stats = statsIter.next(true);

					if(stats != NULL)
					{
						cout << "Min inter-packet gap       : " << stats->minInterPacketGapUS()/1000.0 << " ms" << endl;

						struct timeval totalGaps = stats->totalInterPacketGaps();
						double totalGapsMS = totalGaps.tv_sec*1000.0 + totalGaps.tv_usec/1000.0;
						unsigned int totNumPacketReceived = stats->totNumPacketsReceived();

						cout << "Max inter-packet gap       : " << stats->maxInterPacketGapUS()/1000.0 << " ms" << endl;
						cout << "Average inter-packet gap   : " << (totNumPacketReceived == 0 ? 0.0 : totalGapsMS/totNumPacketReceived) << " ms"<< endl;
					}
					curQOSRecord = curQOSRecord->fNext;
				}
			}
		}

		cout << endl << endl;
		delete qosRecordHead;
	}
}





